﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using System.IO;
using System.Reflection;
using System.Text;
using WEX.Logging.Interop;

namespace Microsoft.Reputation.Tests.FrontEndCommon
{
    /// <summary>
    /// Helper class for cache related operations on the web server
    /// </summary>
    public static class MrsDeliveryServiceHelper
    {
        public static Guid TestClientId = new Guid("4228ac1a-2e5c-40f1-b193-aa5f078c8e83");
        public static Guid TMGCustomerId = new Guid("0900b81a-6c5f-470d-b7fc-a1a335e56a0d"); // <-- TMG Customer ID.
        public static string CustomerVersion = Environment.OSVersion.VersionString;
        public static Guid TestDeploymentId = new Guid("59867f97-e895-4397-b1e7-5d7c1326da48");
        public static readonly uint[] ExpirationTimes = { 0, 1, 20, 40, 60, 480, 960, 1440, 10080, 20160, 30240, 43200, 172800, 345600, 525600, 1576800 };
        public static long DefaultObjectVersion = 0;
        private static DateTime DefaultOverrideExpiration = new DateTime(9999, 12, 31);
        private static MrsReputationObject falseHitObject = new MrsReputationObject(MrsObjectTypes.Url);
        

        public static MrsReputationObject FalseHitObject
        {
            get
            {
                falseHitObject.Update(Int32.MinValue, null, null, 20);
                return falseHitObject;
            }
        }

        /// <summary>
        /// Calls iisreset on the webserver remotely to clear the cache.
        /// </summary>
        /// <param name="machineName">Name of the web server machine on which cache needs to be cleared</param>
        public static void ClearCache(string machineName)
        {
            Process process = new Process();
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.FileName = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "psexec");
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.Arguments = @"/acceptEula \\" + machineName + " iisreset";
            process.Start();
            StreamReader reader = process.StandardOutput;
            while (!reader.EndOfStream)
            {
                Log.Comment(reader.ReadLine().Trim());
            }
        }

        /// <summary>
        /// Makes a LooukupCall with NULL Filter.
        /// </summary>
        /// <param name="requests">List of MrsLookupRequestEx objects</param>
        /// <param name="endpoint">URL of the DeliveryService we need to query.</param>
        /// <returns>List of MrsLookupResponse</returns>
        public static IList<MrsLookupResponse> MakeLookupCallWithNullFilter(IList<MrsLookupRequest> requests, string endpoint)
        {
            MrsFilter filter = new MrsFilter(null, null, null, null);
            IList<MrsLookupResponse> lookupResponses;

            MrsDeliveryServiceProvider deliveryServiceClient = new MrsDeliveryServiceProvider(TMGCustomerId, "FE Tests", TestDeploymentId, TestClientId, endpoint);

            bool status = deliveryServiceClient.LookupObjectAPI(requests, filter, out lookupResponses);

            return lookupResponses;
        }

        public static IList<MrsLookupResponse> MakeLookupCallWithNullFilter(MrsLookupRequest request, string endpoint)
        {
            IList<MrsLookupRequest> req = new List<MrsLookupRequest>();
            req.Add(request);
            return MakeLookupCallWithNullFilter(req, endpoint);
        }

        private static List<string> GetUrlsFromFile(string filename)
        {
            List<string> urlList = new List<string>();
            try
            {
                StreamReader urlFileReader = File.OpenText(filename);
                string line = String.Empty;
                while ((line = urlFileReader.ReadLine()) != null)
                {
                    urlList.Add(line);
                }
            }
            catch (FileNotFoundException)
            {
                //Log.Comment("Operation cannot be completed", e.Message);
                throw;
            }
            return urlList;
        }

        private static string GetRandomUrl()
        {
            return GetRandomUrls(1)[0];
        }

        public static List<string> GetRandomUrls(int count)
        {
            string UrlFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "TestUrls.txt");
            List<string> urlList = GetUrlsFromFile(UrlFile);

            RandomArrays<string> randListObj = new RandomArrays<string>(urlList);

            return randListObj.GetRandomSubsetSelectAndRemove(count);
        }

        public static MrsLookupRequest BuildRandomUrlLookupRequest()
        {
            return BuildDataBlockRequest(GetRandomUrl());
        }

        public static void BuildRandomUrlLookupRequestAndDBObject(out MrsLookupRequest request, out MrsReputationObject dbObject)
        {
            ServiceDbProvider dbProvider = new ServiceDbProvider(ConfigurationSettings.AppSettings["DataBaseServer"], ConfigurationSettings.AppSettings["DataBaseName"]);
            string url = GetRandomUrl();
            byte[] sha256 = MrsApiHelper.GetShaForUrl(url);

            request = BuildDataBlockRequest(url);
            dbObject = dbProvider.GetObject(sha256);
        }

        public static IList<MrsLookupRequest> BuildDataBlockRequest(List<string> url)
        {
            IList<MrsLookupRequest> requestList = new List<MrsLookupRequest>();
            foreach (string requestUrl in url)
            {
                requestList.Add(BuildDataBlockRequest(requestUrl));
            }

            return requestList;
        }

        public static MrsLookupRequest BuildDataBlockRequest(string url)
        {
            MrsLookupRequest request = null;
            MrsApiHelper mrsApi = new MrsApiHelper(url);
            bool res = mrsApi.GetDataBlockByteArray();
            if (true == res)
            {
                IList<MrsMetadata> searchFields = new List<MrsMetadata>();
                searchFields.Add(new MrsMetadata("DataBlock", mrsApi.dataBlockByte));
                request = new MrsLookupRequest(MrsObjectTypes.Url, searchFields);
            }
            else
            {
                throw new Exception("Unable to create data block for the given url");
            }
            return request;
        }

        public static MrsLookupRequest BuildDataBlockRequest(byte[] sha256)
        {
            if (sha256.Length % 32 != 0)
            {
                throw new ArgumentException("Length of byte array passed is incorrect", sha256.ToString());
            }
            DataBlock block = new DataBlock(true);
            int shaCount = sha256.Length / 32;
            block.shas = sha256;
            block.header.ceHashes = BitConverter.GetBytes((short)shaCount);
            block.header.ceVariants = BitConverter.GetBytes((short)shaCount);
            byte[] dataBlock = block.CreateDataBlock();
            List<MrsMetadata> searchFields = new List<MrsMetadata>();
            searchFields.Add(new MrsMetadata("DataBlock", dataBlock));
            MrsLookupRequest datablockRequest = new MrsLookupRequest(MrsObjectTypes.Url, searchFields);
            return datablockRequest;
        }

        public static MrsLookupRequest BuildDataBlockRequest(List<byte[]> shaList)
        {
            byte[] datablock = new byte[shaList.Count * 32];
            int destinationIndex = 0;
            foreach (byte[] sha in shaList)
            {
                Array.Copy(sha, 0, datablock, destinationIndex, 32);
                destinationIndex += 32;
            }
            return BuildDataBlockRequest(datablock);
        }

        public static uint GetStatusCode(string str)
        {
            TestStatusCodes statCode = new TestStatusCodes();
            return statCode.GetStatusCode(str);
        }

        /// <summary>
        /// Creates a MrsLookupRequest for each variant specified in the Urf file and also sets the expected results as MrsReputationObject
        /// </summary>
        /// <param name="file">Full path of the Urf file that contains variant chain</param>
        /// <param name="requests">out param - List of MrsLookupRequests</param>
        /// <param name="expectedResults">out param - List of MrsReputationObjects as expected results</param>
        public static void CreateRequestsFromUrf(string file, out List<MrsLookupRequest> requests, out List<MrsReputationObject> expectedResults)
        {
            CreateRequestsFromUrf(file, out requests, out expectedResults, MrsDeliveryServiceHelper.DefaultObjectVersion);
        }

        /// <summary>
        /// Creates a MrsLookupRequest for each variant specified in the Urf file and also sets the expected results as MrsReputationObject
        /// </summary>
        /// <param name="file">Full path of the Urf file that contains variant chain</param>
        /// <param name="requests">out param - List of MrsLookupRequests</param>
        /// <param name="expectedResults">out param - List of MrsReputationObjects as expected results</param>
        public static void CreateRequestsFromUrf(string file, out List<MrsLookupRequest> requests, out List<MrsReputationObject> expectedResults, long version)
        {
            Log.Comment("Creating lookup request from Urf file...");
            List<UrfReputationObject> urfObjects = UrfFileParser.GetUrfObjects(file, version);
            MrsLookupRequest lkRequest = MrsDeliveryServiceHelper.BuildDataBlockRequest(urfObjects[0].Url);
            requests = new List<MrsLookupRequest>();
            expectedResults = new List<MrsReputationObject>();

            requests.Add(lkRequest);

            foreach (UrfReputationObject obj in urfObjects)
            {
                expectedResults.Insert(0, MrsReputationObject.TranslateFromUrfReputationObject(obj));
            }
        }

        /// <summary>
        /// Creates a MrsLookupRequest for each variant in the Urf file whose index is passed as param and also sets the expected results as MrsReputationObject
        /// </summary>
        /// <param name="file">Full path of the Urf file that contains variant chain</param>
        /// <param name="index">Array of indices specifying the variants those should be returned in requests and expected results</param>
        /// <param name="requests">out param - List of MrsLookupRequests</param>
        /// <param name="expectedResults">out param - List of MrsReputationObjects as expected results</param>
        public static void CreateRequestsFromUrf(string file, int[] index, out List<MrsLookupRequest> requests, out List<MrsReputationObject> expectedResults)
        {
            Log.Comment("Creating lookup request from Urf file...");
            requests = new List<MrsLookupRequest>();
            expectedResults = new List<MrsReputationObject>();
            List<UrfReputationObject> urfObjects = UrfFileParser.GetUrfObjects(file);

            byte[] datablock = new byte[index.Length * 32];
            int destinationIndex = 0;
            for (int idx = 0; idx < index.Length; idx++)
            {
                Array.Copy(urfObjects[index[idx]].Hash, 0, datablock, destinationIndex, 32);
                destinationIndex += 32;

                MrsReputationObject mrsObj = MrsReputationObject.TranslateFromUrfReputationObject(urfObjects[index[idx]]);
                expectedResults.Insert(0, mrsObj);
            }

            requests.Add(MrsDeliveryServiceHelper.BuildDataBlockRequest(datablock));
        }

        public static void InjectTestDataToDB(string filePath, long version)
        {
            IEnumerable<UrfReputationObject> testData = UrfFileParser.GetUrfObjects(filePath);
            Log.Comment("Injecting test data from " + filePath + " to DB");
            foreach (UrfReputationObject obj in testData)
            {
                obj.Version = version;
                TestDataInjector.InjectObjectData(obj.Hash, obj.Pack(MrsDeliveryServiceHelper.ExpirationTimes), obj.Type, version);
            }
        }

        public static void InjectTestDataToDB(string filePath)
        {
            IEnumerable<UrfReputationObject> testData = UrfFileParser.GetUrfObjects(filePath);
            Log.Comment("Injecting test data from " + filePath + " to DB");
            foreach (UrfReputationObject obj in testData)
            {
                TestDataInjector.InjectObjectData(obj.Hash, obj.Pack(MrsDeliveryServiceHelper.ExpirationTimes), obj.Type, obj.Version);
            }
        }

        public static void DeleteTestDataFromDB(string filePath)
        {
            IEnumerable<UrfReputationObject> testData = UrfFileParser.GetUrfObjects(filePath);
            Log.Comment("Clearing test data from DB");
            foreach (UrfReputationObject obj in testData)
            {
                TestDataInjector.DeleteTestData(obj.Hash);
            }
        }

        public static long ImportOverrideFileToDB(string filePath)
        {
            return ImportOverrideFileToDB(filePath, DefaultOverrideExpiration);
        }

        public static long ImportOverrideFileToDB(string filePath, DateTime expirationDateTime)
        {
            IEnumerable<UrfReputationObject> testData = UrfFileParser.GetUrfObjects(filePath);
            long lastOverrideId = 0;
            Log.Comment("Injecting Overrides data from " + filePath + " to DB");
            foreach (UrfReputationObject obj in testData)
            {
                TestDataInjector.UpdateObjectTableWithOverride(obj.Hash, obj.Pack(MrsDeliveryServiceHelper.ExpirationTimes), obj.Type, obj.Version);
                lastOverrideId = TestDataInjector.InjectOverrideData(obj.Hash, obj.Version, expirationDateTime);
            }
            return lastOverrideId;
        }

        public static long ImportOverrideWithVersionIncrement(string filePath)
        {
            IEnumerable<UrfReputationObject> testData = UrfFileParser.GetUrfObjects(filePath);
            long lastOverrideId = 0;
            Log.Comment("Injecting Overrides data from " + filePath + " to DB");
            foreach (UrfReputationObject obj in testData)
            {
                obj.Version += 1;
                TestDataInjector.UpdateObjectTableWithOverride(obj.Hash, obj.Pack(MrsDeliveryServiceHelper.ExpirationTimes), obj.Type, obj.Version);
                lastOverrideId = TestDataInjector.InjectOverrideData(obj.Hash, obj.Version, DefaultOverrideExpiration);
            }
            return lastOverrideId;
        }

        public static long ImportOverrideFileToDB(string filePath, long version)
        {
            return ImportOverrideFileToDB(filePath, DefaultOverrideExpiration, version);
        }

        public static long ImportOverrideFileToDB(string filePath, DateTime expirationDateTime, long version)
        {
            IEnumerable<UrfReputationObject> testData = UrfFileParser.GetUrfObjects(filePath);
            long lastOverrideId = 0;
            Log.Comment("Injecting Overrides data from " + filePath + " to DB");
            foreach (UrfReputationObject obj in testData)
            {
                obj.Version = version;
                TestDataInjector.UpdateObjectTableWithOverride(obj.Hash, obj.Pack(MrsDeliveryServiceHelper.ExpirationTimes), obj.Type, version);
                lastOverrideId = TestDataInjector.InjectOverrideData(obj.Hash, version, expirationDateTime);
            }
            return lastOverrideId;
        }

        public static void ClearOverrideData()
        {
            ClearOverrideData(new DateTime(9999, 12, 31));
        }

        public static void ClearOverrideData(DateTime expirationTime)
        {
            TestDataInjector dataInj = new TestDataInjector();
            Log.Comment("Clearing Overrides data from DB");
            dataInj.ClearOverrides(expirationTime);
        }
    }
}
